package org.example.springtest.annotation;

import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.*;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;

import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author yutianhong
 * @version 1.0
 * @see org.springframework.core.annotation.MergedAnnotations
 * @since 2025/4/3 10:58
 */
public class MergedAnnotationsTest {
    @Retention(RetentionPolicy.RUNTIME)
    @interface Meta {
        @AliasFor("b")
        String a() default "";

        @AliasFor("a")
        String b() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Meta
    @interface Composed {
        @AliasFor(annotation = Meta.class, attribute = "a")
        String c() default "";

        @AliasFor(annotation = Meta.class, attribute = "b")
        String d() default "";
    }

    @Composed(c = "bar")
    static class Bar {
    }

    @Test
    public void testOverrides() {
        String a = MergedAnnotations.from(Bar.class, SearchStrategy.TYPE_HIERARCHY)
                .get(Meta.class)
                .synthesize()
                .a();
        assertEquals("bar", a);
    }

    // ====================================================================================================
    // ====================================================================================================

    // 测试注解定义
    @Retention(RetentionPolicy.RUNTIME)
    @TestMeta(name = "meta")
    @interface TestComposed {
        String value() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @interface TestInherited {
        String value() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @interface TestMeta {
        String name() default "";
    }

    @TestInherited("class")
    static class Parent {
    }

    static class Child extends Parent {
    }

    @TestComposed("direct")
    static class AnnotatedClass {
    }

    @TestMeta(name = "methodMeta")
    @TestComposed("method")
    static class AnnotatedMethodClass {
        @TestComposed("methodValue")
        public void annotatedMethod() {
        }
    }

    // 测试isPresent方法
    @Test
    public void testIsPresent_WhenAnnotationExists() {
        MergedAnnotations merged = MergedAnnotations.from(AnnotatedClass.class);
        assertTrue(merged.isPresent(TestComposed.class));
    }

    @Test
    public void testIsPresent_WhenAnnotationNotExists() {
        MergedAnnotations merged = MergedAnnotations.from(Child.class);
        assertFalse(merged.isPresent(TestMeta.class));
    }

    // 测试isDirectlyPresent方法
    @Test
    public void testIsDirectlyPresent_WhenDirectlyPresent() {
        MergedAnnotations merged = MergedAnnotations.from(AnnotatedClass.class);
        assertTrue(merged.isDirectlyPresent(TestComposed.class));
    }

    @Test
    public void testIsDirectlyPresent_WhenMetaPresent() {
        MergedAnnotations merged = MergedAnnotations.from(AnnotatedClass.class);
        assertFalse(merged.isDirectlyPresent(TestMeta.class));
    }

    // 测试get方法
    @Test
    public void testGet_WithNearestSelector() {
        MergedAnnotation<TestComposed> annotation = MergedAnnotations.from(AnnotatedClass.class)
                .get(TestComposed.class);
        assertTrue(annotation.isPresent());
        assertEquals("direct", annotation.getString("value"));
    }

    @Test
    public void testGet_WithPredicate() {
        MergedAnnotation<TestMeta> annotation = MergedAnnotations.from(AnnotatedClass.class)
                .get(TestMeta.class, a -> a.getString("name").equals("meta"));
        assertTrue(annotation.isPresent());
    }

    // 测试stream方法
    @Test
    public void testStream_WithTypeFilter() {
        MergedAnnotations merged = MergedAnnotations.from(AnnotatedClass.class);
        Stream<MergedAnnotation<TestMeta>> stream =
                merged.stream(TestMeta.class);
        assertEquals(1, stream.count());
    }

    @Test
    public void testStream_AllAnnotations() {
        MergedAnnotations merged = MergedAnnotations.from(AnnotatedClass.class);
        Stream<MergedAnnotation<Annotation>> stream = merged.stream();
        assertTrue(stream.anyMatch(a -> Objects.equals(a.getType(), TestComposed.class)));
    }

    // 测试不同搜索策略
    @Test
    public void testFrom_WithInheritedStrategy() {
        MergedAnnotations merged = MergedAnnotations.from(
                Child.class, SearchStrategy.INHERITED_ANNOTATIONS);
        assertTrue(merged.isPresent(TestInherited.class));
    }

    @Test
    public void testFrom_WithTypeHierarchyStrategy() {
        MergedAnnotations merged = MergedAnnotations.from(
                Child.class, SearchStrategy.TYPE_HIERARCHY);
        assertTrue(merged.isPresent(TestInherited.class));
    }

    // 测试方法注解
    @Test
    public void testFrom_AnnotatedMethod() throws Exception {
        Method method = AnnotatedMethodClass.class.getMethod("annotatedMethod");
        MergedAnnotations merged = MergedAnnotations.from(method);
        assertTrue(merged.isPresent(TestComposed.class));
        assertEquals("methodValue", merged.get(TestComposed.class).getString("value"));
    }

    // 测试重复注解容器
    @Test
    public void testFrom_WithRepeatableContainers() {
        RepeatableContainers containers = RepeatableContainers.standardRepeatables();
        MergedAnnotations merged = MergedAnnotations.from(
                AnnotatedClass.class, SearchStrategy.DIRECT, containers, AnnotationFilter.PLAIN);
        assertNotNull(merged);
    }

    // 测试enclosing类搜索策略
    @Test
    public void testSearchStrategy_TypeHierarchyAndEnclosing() {
        MergedAnnotations merged = MergedAnnotations.from(
                AnnotatedClass.class, SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES);
        assertNotNull(merged);
    }
}
